#include "Chat.h"
#include "log.h"
#include <stdlib.h>
#include <sys/file.h>

static long Chat_ObtenerId(unsigned long numeroUsuario, unsigned long idConversacion){
    return numeroUsuario + (idConversacion<<16);
}

static bool Chat_VerificarExistenciaUsuario(long numeroUsuario){
    if(numeroUsuario <= 0)
	return false;

    FILE* archivo = fopen(ARCHIVO_USUARIOS, "r");
    if(archivo == NULL)
	return false;
    int encontrado = false;

    if(flock(fileno(archivo),LOCK_SH) == -1){
	log(stdout, "Error: no se puede bloquear el archivo %s\n", ARCHIVO_USUARIOS);
    }
    else{
	//Leer el archivo de a un registro por vez
	while(!feof(archivo) && !encontrado){
	    char leido[20];
	    if(fgets(leido, 19, archivo) != NULL)
		encontrado = (atoi(leido) == numeroUsuario);
	}
	flock(fileno(archivo),LOCK_UN);
    }
    fclose(archivo);
    return encontrado;
}

static long Chat_ProximaIdConversacion(){
    long proximoNumero = 0;
    FILE* archivo = fopen(ARCHIVO_ID_CONVERSACION, "r+");
    if(archivo == NULL)
	return proximoNumero;

    if(flock(fileno(archivo),LOCK_EX) == -1){
	log(stdout, "Error: no se puede bloquear el archivo %s\n", ARCHIVO_ID_CONVERSACION);
    }
    else{
	char leido[20];
	if(fgets(leido, 19, archivo) != NULL)
	    proximoNumero = atoi(leido);
	
	fseek(archivo, 0, SEEK_SET);
	fprintf(archivo, "%ld", proximoNumero+1);
	flock(fileno(archivo),LOCK_UN);
    }
    fclose(archivo);
    return proximoNumero;
}

Sesion* ChatIniciarSesion(unsigned long numeroUsuario){

    if(!Chat_VerificarExistenciaUsuario(numeroUsuario)){
	log(stdout, "No existe el usuario %ld.\n", numeroUsuario);
	return NULL;
    }

    Sesion *sesion = (Sesion*)malloc(sizeof(Sesion));
    sesion->numeroUsuario = numeroUsuario;
    sesion->info = new InformacionChat();
    return sesion;
}

void ChatCerrarSesion(Sesion* sesion){
    if(sesion){
	delete sesion->info;
	free(sesion);
    }
    else{
	log(stdout, "Error en ChatCerrarSesion: el handler pasado es nulo. Abortando.");
	exit(EXIT_HANDLER_NULO);
    }
}

std::vector<long> ChatListarTodasLasConversaciones(Sesion* sesion){
    std::vector<long> l;
    if(!sesion)
	return l;
    return sesion->info->obtenerListaDeConversaciones();
}

long ChatCrearConversacion(Sesion* sesion){
    if(!sesion)
	return -1;

    long id= Chat_ProximaIdConversacion();

    sesion->info->agregarConversacion(id);
    return id;
}

bool ChatUnirseAConversacion(Sesion* sesion,long id){
    if(!sesion){
	log(stdout, "Error en ChatUnirseAConversacion: la sesion es nula.");
	return false;
    }

    if(sesion->info->existeConversacion(id)){
	InformacionConversacion info(sesion->numeroUsuario,id);
	info.getListaParticipantes()->agregarElemento(sesion->numeroUsuario);
	return true;
    }
    else{
	log(stdout, "Error al unirse a conversacion %ld. No existe.\n", id);
	return false;
    }
}

void ChatCerrarConversacion(Sesion* sesion,long id){
    if(!sesion){
	log(stdout, "Error en ChatCerrarSesion: la sesion es nula.");
	return;
    }
    InformacionConversacion info(sesion->numeroUsuario,id);
    info.getListaParticipantes()->eliminarElemento(sesion->numeroUsuario);
    
    if(info.getListaParticipantes()->size() == 0)
	sesion->info->eliminarConversacion(id); //eliminarla solo si no quedan usuarios
}

InformacionConversacion* ChatObtenerDatosDeConversacion(long numeroUsuario, long idConversacion){
    return new InformacionConversacion(numeroUsuario,idConversacion);
}

void ChatLiberarDatosDeConversacion(InformacionConversacion* conversacion){
    delete conversacion;
}

bool ChatEnviarMensaje(InformacionConversacion* conversacion, std::string mensaje){
    bool resultado = false;
    if(!conversacion)
	return false;
    std::vector<long> lista = conversacion->getListaParticipantes()->obtenerLista();
    unsigned int i=0;
    while(i<lista.size()){
	log(stdout, "Envio al usuario [%ld]\n", lista[i]);
	resultado = conversacion->getColaEmisora()->
	    enviar(Chat_ObtenerId(lista[i], conversacion->getIdConversacion()), mensaje);
	i++;
    }
    return resultado != -1;
}

std::string ChatRecibirMensaje(InformacionConversacion* conversacion){
    return conversacion->getColaReceptora()->
	recibir(Chat_ObtenerId(conversacion->getIdUsuario(), conversacion->getIdConversacion()));
}
